﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Threading;

using Boggle;

namespace Boggle.Client
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        /// <summary>
        /// The boggle game.
        /// </summary>
        private readonly Game game;

        /// <summary>
        /// Timer whose job it is to advance the game periodically.
        /// </summary>
        private readonly DispatcherTimer timer;

        /// <summary>
        /// A collection of all the dictionaries which can be used.
        /// </summary>
        private readonly List<IBoggleDictionary> dictionaries;

        /// <summary>
        /// Creates a new windows with a game in a setup state.
        /// </summary>
        public MainWindow()
        {
            InitializeComponent();

            new WindowResizer(this,
                new WindowBorder(BorderPosition.Top, top),
                new WindowBorder(BorderPosition.TopRight, topRight),
                new WindowBorder(BorderPosition.Right, right),
                new WindowBorder(BorderPosition.BottomRight, bottomRight),
                new WindowBorder(BorderPosition.Bottom, bottom),
                new WindowBorder(BorderPosition.BottomLeft, bottomLeft),
                new WindowBorder(BorderPosition.Left, left),
                new WindowBorder(BorderPosition.TopLeft, topLeft)
            );
			
			titleBar.MouseLeftButtonDown += (o, e) => DragMove();
			
			dictionaries = DictionaryEnumerator.LoadDictionariesFromDirectory(Environment.CurrentDirectory).OrderBy(d => d.Name).ToList();

            if (dictionaries.Count < 1)
            {
                throw new Exception("Unable to find/create any dictionary types");
            }

            timer = new DispatcherTimer { Interval = TimeSpan.FromMilliseconds(200) };
            timer.Tick += new EventHandler(timer_Tick);

            game = new Game(new BoggleGrid
            (
                new Die[4, 4]
				{
					{
						new Die("a", "a", "e", "e", "g", "n"),
						new Die("e", "e", "g", "h", "n", "w"),
						new Die("e", "i", "o", "s", "s", "t"),
						new Die("a", "b", "b", "j", "o", "o")
					},
					{
						new Die("h", "l", "n", "n", "r", "z"),
						new Die("a", "o", "o", "t", "t", "w"),
						new Die("d", "i", "s", "t", "t", "y"),
						new Die("e", "e", "i", "n", "s", "u")
					},
					{
						new Die("d", "e", "l", "r", "v", "y"),
						new Die("a", "c", "h", "o", "p", "s"),
						new Die("c", "i", "m", "o", "t", "u"),
						new Die("e", "h", "r", "t", "v", "w")
					},
					{
						new Die("e", "l", "r", "t", "t", "y"),
						new Die("a", "f", "f", "k", "p", "s"),
						new Die("d", "e", "i", "l", "r", "x"),
						new Die("h", "i", "m", "n", "qu", "u")
					}
				}
            ), dictionaries.First());

            SetupCommandBindings();
            DataContext = game;
        }

        /// <summary>
        /// Wires up commands to event handlers and adds the bindings to the window.
        /// </summary>
        private void SetupCommandBindings()
        {
            var closeCommand = new CommandBinding(ApplicationCommands.Close);
            closeCommand.Executed += new ExecutedRoutedEventHandler(closeCommand_Executed);
            CommandBindings.Add(closeCommand);

            var startGameCommand = new CommandBinding(BoggleCommands.StartGameCommand);
            startGameCommand.CanExecute += new CanExecuteRoutedEventHandler(startGameCommand_CanExecute);
            startGameCommand.Executed += new ExecutedRoutedEventHandler(startGameCommand_Executed);
            CommandBindings.Add(startGameCommand);

            var submitWordCommand = new CommandBinding(BoggleCommands.SubmitWordCommand);
            submitWordCommand.CanExecute += new CanExecuteRoutedEventHandler(submitWordCommand_CanExecute);
            submitWordCommand.Executed += new ExecutedRoutedEventHandler(submitWordCommand_Executed);
            CommandBindings.Add(submitWordCommand);

            var endGameCommand = new CommandBinding(BoggleCommands.EndGameCommand);
            endGameCommand.CanExecute += new CanExecuteRoutedEventHandler(endGameCommand_CanExecute);
            endGameCommand.Executed += new ExecutedRoutedEventHandler(endGameCommand_Executed);
            CommandBindings.Add(endGameCommand);

            var newGameCommand = new CommandBinding(BoggleCommands.NewGameCommand);
            newGameCommand.CanExecute += new CanExecuteRoutedEventHandler(newGameCommand_CanExecute);
            newGameCommand.Executed += new ExecutedRoutedEventHandler(newGameCommand_Executed);
            CommandBindings.Add(newGameCommand);

            var viewOptionsCommand = new CommandBinding(BoggleCommands.ViewOptionsCommand);
            viewOptionsCommand.CanExecute += new CanExecuteRoutedEventHandler(viewOptionsCommand_CanExecute);
            viewOptionsCommand.Executed += new ExecutedRoutedEventHandler(viewOptionsCommand_Executed);
            CommandBindings.Add(viewOptionsCommand);

            var minimizeWindowCommand = new CommandBinding(BoggleCommands.MinimizeWindowCommand);
            minimizeWindowCommand.Executed += new ExecutedRoutedEventHandler(minimizeWindowCommand_Executed);
            CommandBindings.Add(minimizeWindowCommand);
        }

        #region Command Event Handlers

        /// <summary>
        /// Closes the window.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void closeCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            Close();
        }

        /// <summary>
        /// Starts a new game.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void newGameCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            game.NewGame();
        }

        /// <summary>
        /// Determines whether or not we can start a new game.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void newGameCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = game.CurrentState == Game.GameState.Ended;
        }

        /// <summary>
        /// Ends the current game in progress.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void endGameCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            game.EndGame();
        }

        /// <summary>
        /// Determines whether or not we can end the current game.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void endGameCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = game.CurrentState == Game.GameState.InProgress;
        }

        /// <summary>
        /// Submits the current word.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void submitWordCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            game.SubmitWord();
            lstWords.ScrollIntoView(game.SelectedWord);
            txtWord.Focus();
        }

        /// <summary>
        /// Determines whether or not we can submit the current word.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void submitWordCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = game.CurrentState == Game.GameState.InProgress && 
                           !String.IsNullOrEmpty(game.CurrentWord);
        }

        /// <summary>
        /// Starts the current game.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void startGameCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            game.Start(DateTime.Now);
            timer.Start();
			txtWord.Focus();
        }

        /// <summary>
        /// Determines whether we can start the current game.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void startGameCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = game.CurrentState == Game.GameState.Setup;
        }

        /// <summary>
        /// Pops up the options dialog.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void viewOptionsCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            var optionsWindow = new OptionsWindow(
                dictionaries,
                game.Dictionary)
                {
                    Owner = this
                };

            var result = optionsWindow.ShowDialog();

            if (result.HasValue && result.Value)
            {
                game.Dictionary = optionsWindow.SelectedDictionary;
            }
        }

        /// <summary>
        /// Determines whether or not we can display the options dialog.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void viewOptionsCommand_CanExecute(object sender, CanExecuteRoutedEventArgs e)
        {
            e.CanExecute = game.CurrentState == Game.GameState.Setup;
        }

        /// <summary>
        /// Minimizes this window.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        void minimizeWindowCommand_Executed(object sender, ExecutedRoutedEventArgs e)
        {
            WindowState = WindowState.Minimized;
        }

        #endregion

        /// <summary>
        /// Advances the game's time.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void timer_Tick(object sender, EventArgs e)
        {
            if (!timer.IsEnabled)
            {
                return;
            }

            if (!game.Next(DateTime.Now))
            {
                timer.Stop();
				game.NewGame();
            }
        }
    }
}
